﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;

namespace TripLOAMobile
{
    /// <summary>
    /// An AddressBox is an extended textbox to input an address. 
    /// An address contains the following fields:
    /// The address is considered constituted by:
    /// <list type="bullet">
    /// <item><term>Street</term></item>
    /// <item><term>City</list>
    /// <item><term>ZipCode</term></item>
    /// <item><term>Province</term></item>
    /// <item><term>Nation</term></item>
    /// The control distinguishes between what is showed to the user and what is 
    /// the full address, so it is allowed to use a programmer defined format 
    /// for visualization, and a different one for processing.
    /// Each field is accessible also as a standalone field.
    /// 
    /// The AddressBox must be used jointly to <see cref="AddressPanel"/> class,
    /// which provides an easy to use filling interface.
    /// </summary>
    public partial class AddressBox : UserControl
    {
        /// <summary>
        /// Fields identifiers.
        /// </summary>
        private enum Fields
        {
            SRT = 0, ///street
            CTY = 1, ///city
            ZIP = 2, ///zipcode
            PVC = 3, ///province
            NTN = 4  ///nation
        }

        private const string EMPTY_STRING = "";

        string m_FullFormat = "{0}, {1}, {2}, {3}, {4}";
        string m_ViewFormat = "{0}, {2} {1} {3}, {4}";

        //string[] m_FullBlocks; //List of blocks constituting the full text string
        string[] m_Blocks; //List of blocks constituting the text string

        string[] m_fields;

        AddressPanel m_Filler;

        /// <summary>
        /// Initializes a new instance of the <see cref="AddressBox"/> class.
        /// </summary>
        public AddressBox()
        {
            InitializeComponent();
            m_fields = new string[5];
        }

        /// <summary>
        /// Gets the default size.
        /// </summary>
        /// <value>The default size.</value>
        protected Size DefaultSize { get { return new Size(100, 21); } }

        /// <summary>
        /// Gets the street field.
        /// </summary>
        /// <value>The street.</value>
        public string Street { get { return m_fields[(int)Fields.SRT] ?? ""; } }
        /// <summary>
        /// Gets the city field.
        /// </summary>
        /// <value>The city.</value>
        public string City { get { return m_fields[(int)Fields.CTY] ?? ""; } }
        /// <summary>
        /// Gets the zip code field.
        /// </summary>
        /// <value>The zip code.</value>
        public string ZipCode { get { return m_fields[(int)Fields.ZIP] ?? ""; } }
        /// <summary>
        /// Gets the province field.
        /// </summary>
        /// <value>The province.</value>
        public string Province { get { return m_fields[(int)Fields.PVC] ?? ""; } }
        /// <summary>
        /// Gets the nation field.
        /// </summary>
        /// <value>The nation.</value>
        public string Nation { get { return m_fields[(int)Fields.NTN] ?? ""; } }

        /// <summary>
        /// Gets or sets the text address to be showed.
        /// </summary>
        /// <value></value>
        /// <returns>The text address to be showed.</returns>
        public override string Text { get { return textBox.Text; } }
        /// <summary>
        /// Gets or sets the text format for the showed address.
        /// Into the string the following placeholders are introduced:
        /// <list type="bullet">
        /// <item><term>SRT</term><description> for the street field</description></item>
        /// <item><term>CTY</term><description> for the city field</description></item>
        /// <item><term>ZIP</term><description> for the zipcode field</description></item>
        /// <item><term>PVC</term><description> for the province field</description></item>
        /// <item><term>NTN</term><description>for the nation field</description></item>
        /// </list>
        /// Each placeholder can occurr also more than once.
        /// Moreover a concept of block is defined, which represent a part of format strictly related.
        /// A block is considered valid (and so is filled) ONLY IF ALL the fields contained in it are not empty.
        /// Two block adiacent are separed by a '|' character.
        /// An example of string format is:
        ///     "SRT, |ZIP - |CTY |PVC |(NTN)"
        /// which produces results like:
        ///     "Via Buonarroti, 56100 - Pisa PI (Italy)"
        ///     "56100 - Pisa PI (Italy)"
        ///     "Via Buonarroti, Pisa PI (Italy)"
        ///     "Via Buonarroti, 56100 - Pisa (Italy)"
        ///     "Pisa PI (Italy)"
        ///     "Pisa (Italy)"
        /// related to which field is really filled and whic instead is left empty.
        /// </summary>
        /// <value>The text format.</value>
        public string TextFormat { get { return Format2String(); } set { m_ViewFormat = String2Format(value); } }

        /// <summary>
        /// Gets the full text address.
        /// </summary>
        /// <value>The full text address.</value>
        public string FullText { get { return string.Format(m_FullFormat, m_fields); } }
        /// <summary>
        /// Gets or sets the text format for the processable address.
        /// Into the string the following placeholders are introduced:
        /// <list type="bullet">
        /// <item><term>SRT</term><description> for the street field</description></item>
        /// <item><term>CTY</term><description> for the city field</description></item>
        /// <item><term>ZIP</term><description> for the zipcode field</description></item>
        /// <item><term>PVC</term><description> for the province field</description></item>
        /// <item><term>NTN</term><description>for the nation field</description></item>
        /// </list>
        /// An example of string format is:
        ///     "NTN,CTY PVC,ZIP,SRT"
        /// which produces a result like 
        ///     "Italy,Pisa PI,56100,Via Buonarroti"
        /// </summary>
        /// <value>The full format.</value>
        public string FullFormat { get { return FullFormat2String(m_FullFormat); } set { m_FullFormat = String2FullFormat(value); } }

        /// <summary>
        /// Gets or sets the related filling panel.
        /// </summary>
        /// <value>The fill panel.</value>
        public AddressPanel FillPanel { get { return m_Filler; } set { m_Filler = value; } }

        /// <summary>
        /// Gets or sets the background color for the control.
        /// </summary>
        /// <value></value>
        /// <returns>A <see cref="T:System.Drawing.Color"/> that represents the background color of the control. The default is the value of the <see cref="P:System.Windows.Forms.Control.DefaultBackColor"/> property.</returns>
        /// <PermissionSet>
        /// 	<IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
        /// </PermissionSet>
        public override Color BackColor { get { return textBox.BackColor; } set { textBox.BackColor = value; } }
        /// <summary>
        /// Gets or sets the foreground color of the control.
        /// </summary>
        /// <value></value>
        /// <returns>The foreground <see cref="T:System.Drawing.Color"/> of the control. The default is the value of the <see cref="P:System.Windows.Forms.Control.DefaultForeColor"/> property.</returns>
        /// <PermissionSet>
        /// 	<IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
        /// </PermissionSet>
        public override Color ForeColor { get { return textBox.ForeColor; } set { textBox.ForeColor = value; } }
        /// <summary>
        /// Gets or sets the font of the text displayed by the control.
        /// </summary>
        /// <value></value>
        /// <returns>The <see cref="T:System.Drawing.Font"/> to apply to the text displayed by the control. The default is the value of the <see cref="P:System.Windows.Forms.Control.DefaultFont"/> property.</returns>
        /// <PermissionSet>
        /// 	<IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
        /// 	<IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
        /// 	<IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence"/>
        /// 	<IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
        /// </PermissionSet>
        public override Font Font { get { return textBox.Font; } set { textBox.Font = value; } }

        /// <summary>
        /// Extracts the format for the full address from a string.
        /// </summary>
        /// <param name="str">The string.</param>
        /// <returns>The format.</returns>
        private string String2FullFormat(string str)
        {
            StringBuilder format = new StringBuilder(str);
            StringBuilder placeHolder = new StringBuilder("{ }");
            for (Fields fld = Fields.SRT; fld <= Fields.NTN; fld++)
            {
                placeHolder[1] = ((int)fld).ToString()[0];
                format = format.Replace(
                    fld.ToString(),
                    placeHolder.ToString());
            }

            return format.ToString();
        }

        /// <summary>
        /// Composes a string that represents the format for the full address (processable address).
        /// </summary>
        /// <param name="format">The format.</param>
        /// <returns>The string.</returns>
        private string FullFormat2String(string format)
        {
            StringBuilder str = new StringBuilder(format);
            
            StringBuilder placeHolder = new StringBuilder("{ }");
            for (Fields fld = Fields.SRT; fld <= Fields.NTN; fld++)
            {
                placeHolder[1] = ((int)fld).ToString()[0];
                str = str.Replace(placeHolder.ToString(), fld.ToString());
            }

            return str.ToString();
        }

        /// <summary>
        /// Extracts the format for the showed address from a string.
        /// </summary>
        /// <param name="str">The string.</param>
        /// <returns>The format.</returns>
        private string String2Format(string str)
        {
            StringBuilder format = new StringBuilder(str);
            StringBuilder placeHolder = new StringBuilder("{ }");
            for (Fields fld = Fields.SRT; fld <= Fields.NTN; fld++)
            {
                placeHolder[1] = ((int)fld).ToString()[0];
                format = format.Replace( fld.ToString(), placeHolder.ToString());
            }

            m_Blocks = format.ToString().Split('|');
            format.Remove(0, format.Length);
            for (int i = 0; i < m_Blocks.Length; i++)
                format.Append('{').Append(i).Append('}');
            
            return format.ToString();
        }

        /// <summary>
        /// Composes a string that represents the format for the showed address.
        /// </summary>
        /// <returns>The string.</returns>
        private string Format2String()
        {
            StringBuilder str = new StringBuilder(64);
            if(m_Blocks.Length != 0){
                str.Append(string.Join("|", m_Blocks));

                StringBuilder placeHolder = new StringBuilder("{ }");
                for (Fields fld = Fields.SRT; fld <= Fields.NTN; fld++)
                {
                    placeHolder[1] = ((int)fld).ToString()[0];
                    str = str.Replace( placeHolder.ToString(), fld.ToString());
                }
            }

            return str.ToString();
        }

        /// <summary>
        /// Updates the address (used by the <see cref="AddressPanel"/> class.
        /// </summary>
        /// <param name="street">The street.</param>
        /// <param name="city">The city.</param>
        /// <param name="zipcode">The zipcode.</param>
        /// <param name="province">The province.</param>
        /// <param name="nation">The nation.</param>
        internal void UpdateAddress(string street, string city, string zipcode, string province, string nation)
        {
            m_fields[(int)Fields.SRT] = street;
            m_fields[(int)Fields.CTY] = city;
            m_fields[(int)Fields.ZIP] = zipcode;
            m_fields[(int)Fields.PVC] = province;
            m_fields[(int)Fields.NTN] = nation;

            StringBuilder text = new StringBuilder(64);

            StringBuilder placeHolder = new StringBuilder("{ }");
            bool empty;
            for (int i = 0; i < m_Blocks.Length; i++)
            {
                empty = false;
                for (Fields fld = Fields.SRT; fld <= Fields.NTN; fld++)
                {
                    placeHolder[1] = ((int)fld).ToString()[0];
                    if (m_Blocks[i].Contains(placeHolder.ToString()) && m_fields[(int)fld].Length == 0)
                    {
                        empty = true;
                        break;
                    }
                }
                if (!empty) text.Append(string.Format(m_Blocks[i], m_fields));
            }
            textBox.Text = text.ToString();
        }

        /// <summary>
        /// Handles the OnGotFocus event, by showing the related <see cref="AddressPanel"/> to simplify the input.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        /// <exception cref="InvalidOperationException"> Throws an exception if no <see cref="AddressPanel"/> is associate too.</exception>
        private void textBox_OnGotFocus(object sender, EventArgs e)
        {
 	        if (m_Filler == null) 
                throw new InvalidOperationException("No fill panel is associated to this control");
            else
            {
                m_Filler.FillingBox = this;
                m_Filler.Show();
                m_Filler.Focus();
            }
            
            base.OnGotFocus(e);
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.Control.Resize"/> event.
        /// </summary>
        /// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
        protected override void OnResize(EventArgs e)
        {
            textBox.Width = Width;
            Height = textBox.Height;
            base.OnResize(e);
        }
    }
}
